<?php

namespace App\Http\Controllers;

use App\Jobs\MakeOrdeJob;
use App\Models\GoodsConfig;
use App\Models\SeckillGood;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redis;

class SeckillController extends Controller
{
    /**
     * 秒杀
     */
    //连接redis
    public $redis;

    public function __construct()
    {
        $this->redis = new \Redis();
        $this->redis->connect('127.0.0.1', 6379);
    }

    ////不是当天的秒杀商品预热库存
    //public function preheatGoods()
    //{
    //	$sameDay = date('Y-m-d');
    //	//开始商品
    //	$preheatGoods = (new SeckillGood())->with('goodsTime')->where('seckill_state', 1)->where('start_time', $sameDay)->get()->toArray();
    //	//循环放入redis
    //	foreach ($preheatGoods as $k => $v) {
    //		//开始时间和结束时间
    //		$startTime = $v['start_time'] . ' ' . intval($v['goods_time'][0]['start_time']) . ':' . '00' . ':' . '00';
    //		$endTime = $v['start_time'] . ' ' . intval($v['goods_time'][0]['start_time'] + $v['goods_time'][0]['continued_time']) . ':' . '00' . ':' . '00';
    //		//dd($v);
    //		$res = (new SeckillGood())->where('id', $v['goods_id'])->update(['end_time' => $endTime]);
    //		//dd($preheatGoods);
    //		//dd($startTime, $endTime);
    //		for ($i = 0; $i <= $v['seckill_count']; $i++) {
    //			//将数据存放到列表中
    //			Redis::lPush('number_' . $v['goods_id'], 1);
    //		}
    //		//根据结束时间-当前时间设置为过期时间，缓存消失则活动结束
    //		//存这条秒杀商品的开始时间
    //		Redis::set('start_time_' . $v['goods_id'], strtotime($startTime), strtotime($endTime) - time());
    //	}
    //}

    /**
     * 秒杀
     */
    public function seckillGoods(Request $request)
    {
        //用户id
        $userId = $request->user_id;
        //秒杀商品id
        $seckillId = $request->post('seckillId');

        //当前时间
        $date = time();
        //首先获取活动开始时间
        $startTime = Redis::get('start_time_' . $seckillId);

        //判断这条商品是否结束，缓存到期说明结束
        if (!$startTime) {
            //dd($startTime);
            //结束删除秒杀商品队列
            Redis::del('number_' . $seckillId);
            return ['code' => 400, 'message' => '活动已结束', 'data' => null];
        }
        //如果当前时间小于开始时间，视为活动未开始
        if ($date < $startTime) {
            //dd($startTime);
            return ['code' => 400, 'message' => '活动未开始', 'data' => null];
        }


        //一个用户一天只能抢一次 拼当天日期
        $day = date('Y-m-d');
        //用户唯一,发票
        $userKey = 'userKey' . $seckillId . $day;
        //进行当前用户是否在抢成功的队列里面  无序集合
        $user = Redis::sIsMember($userKey, $userId);
        //如果你在这里面，就抢了 （每个用户只能买一次）
        if ($user) {
            return ['code' => 400, 'message' => '你已经买过了', 'data' => null];
        }


        //秒杀商品的队列
        $numberKey = 'number_' . $seckillId;
        //获取库存数量的长度
        $goodsNum = Redis::lLen($numberKey);
        //判断库存是否存在 ，防止超卖
        if ($goodsNum <= 0) {
            return ['code' => 400, 'message' => '已经抢光了', 'data' => null];
        }


        try {
            //Redis Watch 命令用于监视一个(或多个) key ，如果在事务执行之前这个(或这些) key 被其他命令所改动，那么事务将被打断
            //记录监听的事务 库存key 用户key
            Redis::watch(array($numberKey, $userKey));
            //开启事务
            Redis::multi();

            //左进右出，减库存，把队列里卖弄的数据从右边
            Redis::rPop($numberKey);
            //把当前这件秒杀商品的userId存储到队列里无序集合
            Redis::sAdd($userKey, $userId);
            //执行事务，失败走catch，进行回滚
            Redis::exec();

            //消息队列，生成订单，入订单表
            $this->dispatch(new MakeOrdeJob($userId, $seckillId));
        } catch (\Exception $exception) {
            //事务失败，进行回滚
            Redis::discard();
            return ['code' => 400, 'message' => '抢购失败', 'data' => null];
        }
    }

    /**
     * 秒杀商品展示
     */
    public function seckillGoodsShow()
    {
        $sameDay = date('Y-m-d');
        $data = Redis::get('seckill');
        if ($data != null) {
            return json_decode($data, true);
        }
        $con = (new SeckillGood())->where('start_time', $sameDay)->get()->toArray();
        if ($con) {
            Redis::setex('seckill', 3600, json_encode($con, JSON_UNESCAPED_UNICODE));
            return ['code' => 200, 'mes' => 'success', 'data' => $con];
        }
        return ['code' => 2000, 'mes' => 'error', 'data' => []];
    }

    /**
     * 时间段
     */
    public function time()
    {
        $res = (new GoodsConfig())->orderBy('start_time', 'asc')->get()->toArray();
        if ($res) {
            return ['code' => 200, 'mes' => 'success', 'data' => $res];
        }
        return ['code' => 2000, 'mes' => 'error', 'data' => []];
    }

    /**
     * 根据时间检索
     */
    public function selectTime(Request $request)
    {
        $timeId = $request->post('timeId');
        if ($timeId == '') {
            return json_encode(['code' => 4000, 'mes' => 'error', 'data' => []]);
        }
        //根据时间段的id，查询出秒杀商品信息，然后再查询出当天的商品
        $sameDay = date('Y-m-d');
        $con = (new SeckillGood())->where('time_slot', $timeId)->where('start_time', $sameDay)->get()->toArray();
        if ($con) {
            return ['code' => 200, 'mes' => 'error', 'data' => $con];
        }
        return ['code' => 2000, 'mes' => 'error', 'data' => []];
    }
}